package com.core.redis;

import com.core.utils.SpringContextUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.util.Assert;

import java.util.concurrent.TimeUnit;

/**
 * 自定义redisLock方便使用
 *
 * @author gang
 * @see org.redisson.RedissonLock
 * @see AutoCloseable
 */
public class RedisLock implements AutoCloseable {

    private static final int DEFAULT_WAIT_TIME = 5;
    private static final int DEFAULT_LEASE_TIME = 30;
    private static final TimeUnit DEFAULT_UNIT = TimeUnit.SECONDS;

    private static final Object SYNC_OBJ = new Object();
    private static RedissonClient redissonClient;

    private final String key;
    private boolean lockSuccess;


    public static RedisLock create(String lockKey) {
        return create(lockKey, DEFAULT_LEASE_TIME, DEFAULT_UNIT);
    }

    public static RedisLock create(String lockKey, int leaseTime, TimeUnit unit) {
        init();
        RedisLock redisLock = new RedisLock(lockKey);
        redisLock.lock(leaseTime, unit);
        redisLock.lockSuccess = true;
        return redisLock;
    }


    public static RedisLock tryLock(String lockKey) {
        return tryLock(lockKey, DEFAULT_WAIT_TIME, DEFAULT_LEASE_TIME, DEFAULT_UNIT);
    }

    public static RedisLock tryLock(String lockKey, int waitTime, int leaseTime, TimeUnit unit) {
        init();
        RedisLock redisLock = new RedisLock(lockKey);
        redisLock.tryLock(waitTime, leaseTime, unit);
        return redisLock;
    }

    private static void init() {
        if (redissonClient == null) {
            synchronized (SYNC_OBJ) {
                if (redissonClient == null) {
                    redissonClient = SpringContextUtil.getApplicationContext().getBean(RedissonClient.class);
                }
            }
        }
    }


    private RedisLock(String lockKey) {
        Assert.notNull(redissonClient, "请先设置redisson属性");
        this.key = lockKey;
    }

    private void lock(int leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(key);
        Assert.notNull(lock, "获取redis锁失败");
        lock.lock(leaseTime, unit);
        this.lockSuccess = true;
    }

    private void tryLock(int waitTime, int leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(key);
        try {
            this.lockSuccess = lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            throw new RuntimeException("获取redis锁失败", e);
        }

    }


    @Override
    public void close() throws Exception {
        if (lockSuccess) {
            RLock lock = redissonClient.getLock(key);
            lock.unlock();
        }
    }

    public boolean isLockSuccess() {
        return lockSuccess;
    }
}
